# -*- coding: utf-8 -*-
import random
import time
import os
import cv2
import logging
import numpy as np
# 导入新的VNC客户端实现
from pytest_vnc import VNCClient

import keyboard
# 固定显示窗口大小为屏幕分辨率
from YJS64 import YJS
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
import random

class VNC:
    def __init__(self, host, port, password=None):
        self.client = None
        self.host = host
        self.port = int(port)
        self.password = password

        VID, PID = "0xC216", "0x0102"
        self.yjs = YJS(VID, PID)
        self.yjs.open_yjs("./model/msdk.dll", SCREEN_WIDTH, SCREEN_HEIGHT)

    def _human_delay(self, min_ms=50, max_ms=100):
        """随机延迟模拟人类操作"""
        time.sleep(random.uniform(min_ms / 1000, max_ms / 1000))

    def _clamp_coordinates(self, x, y):
        """确保坐标为非负值"""
        return (max(0, x), max(0, y))

    def _bezier_curve(self, start, end, control=None, steps=10):
        """生成贝塞尔曲线路径"""
        if control is None:
            control = (
                (start[0] + end[0]) // 2 + random.randint(-50, 50),  # 减少随机偏移
                (start[1] + end[1]) // 2 + random.randint(-50, 50)
            )

        points = []
        for t in [i / steps for i in range(steps + 1)]:
            x = (1 - t) ** 2 * start[0] + 2 * (1 - t) * t * control[0] + t ** 2 * end[0]
            y = (1 - t) ** 2 * start[1] + 2 * (1 - t) * t * control[1] + t ** 2 * end[1]
            points.append((int(x), int(y)))
        return points


    def connect(self, retry=3):
        password_info = "有密码" if self.password else "无密码"
        logging.info(f"VNC正在尝试连接 {self.host}:{self.port} ({password_info})")
        if self.client is not None:
            logging.info(f"VNC已经连接到 {self.host}:{self.port}，无需重复连接")
            return True

        for attempt in range(1, retry + 1):
            try:
                # 使用新的VNC客户端
                logging.info(f"第 {attempt} 次连接尝试...")
                self.client = VNCClient(ip=self.host, port=self.port, timeout=5, password=self.password)
                self.client.connect()

                # 等待客户端准备就绪
                start_time = time.time()
                while not self.client.is_ready() and (time.time() - start_time) < 10:
                    time.sleep(0.1)

                if not self.client.is_ready():
                    raise Exception("VNC客户端在10秒内未准备就绪")

                # 验证vnc是否成功连接（获取一帧数据验证）
                frame = self.capture()
                if frame is None:
                    raise Exception("VNC连接成功但无法获取屏幕数据")

                logging.info(f"VNC已连接到 {self.host}:{self.port}")
                return True
            except Exception as e:
                logging.error(f"VNC第 {attempt} 次连接失败: {e}")
                if self.client:
                    try:
                        self.client.close()
                    except:
                        pass
                    self.client = None
                if attempt < retry:
                    time.sleep(1)

        logging.error(f"VNC经过 {retry} 次尝试，仍无法连接到 {self.host}:{self.port}")
        return False

    def close(self):
        if self.client is None:
            logging.warning(f"VNC未建立连接，跳过关闭操作")
            return

        try:
            self.client.close()
            logging.info(f"VNC已安全断开 {self.host}:{self.port}")
        except Exception as e:
            logging.error(f"VNC关闭连接时发生错误: {e}")
        finally:
            self.client = None  # 清理状态

    # VNC服务器的屏幕截图
    def capture(self):
        """VNC服务器截图 - 保色清晰化处理（保持原始颜色+提升清晰度）"""
        try:
            # 使用新客户端获取帧数据
            frame = self.client.get_frame(copy=False)
            if frame is None:
                logging.warning("获取帧数据失败")
                return None
            return frame

        except Exception as e:
            logging.error(f"VNC截图失败: {e}")
            return None

    def key_press(self, key, times=1):
        """按下并弹起按键，支持多次操作"""
        for _ in range(times):
            self.key_down(key)
            self._human_delay(50, 100)  # 按的延迟
            # 10%  概率加多一点延迟，模拟人类思考
            if random.random() < 0.1:
                self._human_delay(20, 40)
            self.key_up(key)
            if times > 1:
                self._human_delay(50, 100)  # 多次按键间额外延迟

    def key_down(self, key):
        """按下按键"""
        try:
            # 先释放相反方向的按键
            if key == keyboard.Right:
                self.key_up(keyboard.Left)
            elif key == keyboard.Left:
                self.key_up(keyboard.Right)
            elif key == keyboard.Up:
                self.key_up(keyboard.Down)
            elif key == keyboard.Down:
                self.key_up(keyboard.Up)

            self.yjs.key_down(key)
        except Exception as e:
            print(f"按键按下失败: {e}")

    def key_up(self, key):
        """弹起按键"""
        try:
            self.yjs.key_up(key)
            print(f"弹起：{key}")
        except Exception as e:
            print(f"按键弹起失败: {e}")

    def mouse_move(self, x, y):

        try:
            # 确保目标坐标合法
            x, y = self._clamp_coordinates(x, y)
            if hasattr(self, 'current_mouse_pos'):
                points = self._bezier_curve(self.current_mouse_pos, (x, y))
                for point in points:
                    self.yjs.mouse_move3(*point)
                    time.sleep(random.uniform(0.005, 0.01))  # 加快移动速度
            else:
                self.yjs.mouse_move3(x, y)
            self.current_mouse_pos = (x, y)  # 更新位置
        except Exception as e:
            print(f"鼠标移动失败: {e}")

    def mouse_left_click(self):
        """鼠标左键点击"""
        try:
            self.yjs.mouse_left_click()
        except Exception as e:
            print(f"鼠标左键点击失败: {e}")

    def mouse_right_click(self):
        """鼠标右键点击"""
        try:
            self.yjs.mouse_right_click()
        except Exception as e:
            print(f"鼠标右键点击失败: {e}")


def cv_模板匹配(img1, img2):
    # cv2.setNumThreads(0)  # 全局禁用并行处理
    cv2.setUseOptimized(True)  # 关闭SIMD优化(如SSE/AVX),关闭会慢一倍
    s = time.time()
    result = cv2.matchTemplate(img1, img2, cv2.TM_CCOEFF_NORMED)  # 归一化相关系数
    print('cv 模板匹配耗时:', time.time() - s)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    return result, min_val, max_loc


if __name__ == '__main__':
    v = VNC("192.168.29.111", "5900", password="000000")
    print("VNC 连接成功")

    vnc_success = v.connect()

    fps_time = time.time()
    fps = 0
    try:
        while True:
            frame = v.capture()
            if frame is not None:
                # box = (208, 467, 35, 11)
                # # 截取ROI区域
                # mini_map_img = frame[box[1]:box[1] + box[3], box[0]:box[0] + box[2]]
                # cv2.imwrite('pl.png', mini_map_img)
                # print("出来了")
                # n = DXFServer.calculateMovingSpeed(mini_map_img)
                # print("什么贵")
                # print(n)
                # # 读取模板（0表示灰度模式）
                # template = cv2.imread('./model/sy_fb.png')
                #
                # # 关键：修改变量名，不使用v，避免与VNC对象冲突
                # result, max_val, max_loc = cv_模板匹配(mini_map_img, template)
                # # 输出匹配结果
                # print(f"最大匹配值: {max_val:.4f}")  # 越接近1表示匹配度越高
                # print(f"最佳匹配位置: {max_loc}")
                # 90 0  w 5 h 14
                # box = (90, 0, 5, 14)
                # img = mini_map_img[box[1]:box[1] + box[3], box[0]:box[0] + box[2]]
                # cv2.imwrite('i.png', img)  # 请将'saved_image.jpg' 替换为想要保存的图片路径
                # cv2.destroyAllWindows()
                start_time = time.time()

                # 显示帧
                # cv2.putText(mini_map_img, f"FPS: {int(fps)}", (10, 30),
                #             cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
                cv2.imshow("VNC", frame)

                # 计算显示耗时
                end_time = time.time()
                elapsed = (end_time - start_time) * 1000
                print(f"显示一帧耗时: {elapsed:.2f} ms")

                # 计算 FPS，避免除零
                dt = time.time() - fps_time
                fps = 1 / dt if dt > 1e-6 else 0
                fps_time = time.time()

            if cv2.waitKey(1) & 0xFF == 27:  # ESC退出
                break
    finally:
        v.close()
        cv2.destroyAllWindows()
